#include "ByteStream.h"
#include <stdio.h>
#include <string.h>

ByteStream::ByteStream(): segmentlist(0), segmentpos(0)
{
}

ByteStream::~ByteStream()
{
	Clear();
}

void ByteStream::Clear()
{
	ByteSegment *headsegment = segmentlist;

	if (headsegment)
	{
		ByteSegment *segment = headsegment;

		do
		{
			unsigned char *delsegment = (unsigned char*)segment;

			segment = segment->next;

			delete[] delsegment;
		} 
		while(headsegment != segment);
	}

	segmentlist = 0;
	segmentpos = 0;
}

void ByteStream::RemoveFirstSegment()
{
	unsigned char *delsegment = (unsigned char*)segmentlist;

	// If last segment
	if (segmentlist->previous == segmentlist)
	{
		segmentlist = 0;
	}
	else
	{
		ByteSegment *next = segmentlist->next;
		ByteSegment *last = segmentlist->previous;

		next->previous = last;
		last->next = next;

		segmentlist = next;
	}

	delete[] delsegment;
}

void ByteStream::AddSegment(ByteSegment *segment)
{
	if (segmentlist == 0)
	{
		segmentlist = segment;
		segment->next = segment->previous = segment;
	}
	else
	{
		ByteSegment *lastsegment = segmentlist->previous;
		ByteSegment *firstsegment = segmentlist;

		lastsegment->next = segment;
		firstsegment->previous = segment;

		segment->previous = lastsegment;
		segment->next = firstsegment;
	}
}

void ByteStream::Write(const void *data, unsigned int size)
{
	ByteStream::WriteRaw(data, size);
}

// Read from the buffer, and write it into the bytestream
void ByteStream::WriteRaw(const void *data, unsigned int size)
{
	unsigned char* segmentdata = new unsigned char[size + sizeof(ByteSegment)];
	unsigned char* streamdata = segmentdata + sizeof(ByteSegment);
	ByteSegment *segment = (ByteSegment*)segmentdata;

	segment->size = size;
	memcpy(streamdata, data, size);

	AddSegment(segment);
}

bool ByteStream::Eob() const
{
	return segmentlist == 0;
}

unsigned int ByteStream::Read(void *data, unsigned int size)
{
	return ByteStream::ReadRaw(data, size);
}

// Read from the bytestream, and write it to the buffer
unsigned int ByteStream::ReadRaw(void *data, unsigned int size)
{
	unsigned int totalread = 0;

	if (!segmentlist)
		return 0;

	while(size)
	{
		unsigned int leftsize = segmentlist->size - segmentpos;
		unsigned int readsize = leftsize;

		unsigned char *streamdata = (unsigned char*)segmentlist + sizeof(ByteSegment) + segmentpos;
		if (readsize > size)
			readsize = size;

		if (data)
		{
			memcpy(data, streamdata, readsize);
			data = (void*)((unsigned char*)data + readsize);
		}
		totalread += readsize;
		size -= readsize;

		segmentpos += readsize;

		if (segmentpos >= segmentlist->size)
		{
			segmentpos = 0;
			RemoveFirstSegment();
		}

		if (segmentlist == 0)
			break;
	}

	return totalread;
}


// Read data from a file into the bytestream
void ByteStream::ReadFromFile(const char* filename)
{
	FILE *f = fopen(filename, "rb");

	if(f == 0)
		throw "ByteStream: Could not read file";

	unsigned int filesize;

	fseek(f, 0, SEEK_END);
	filesize = ftell(f);
	fseek(f, 0, SEEK_SET);


	unsigned char* segmentdata = new unsigned char[filesize + sizeof(ByteSegment)];
	unsigned char* streamdata = segmentdata + sizeof(ByteSegment);
	ByteSegment *segment = (ByteSegment*)segmentdata;

	segment->size = filesize;
	fread(streamdata, sizeof(unsigned char), filesize, f);

	AddSegment(segment);

	fclose(f);
}


// Write the bytestream data to a file
void ByteStream::WriteToFile(const char *filename)
{
	unsigned char buffer[1024];
	FILE *f = fopen(filename, "wb");

	if(f == 0)
		throw "ByteStream: Could not write to file";

	while(1)
	{
		unsigned int readbytes = ReadRaw(buffer, sizeof(buffer));

		if (readbytes == 0)
			break;

		fwrite(buffer, sizeof(unsigned char), readbytes, f);
	}

	fclose(f);
}